home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume24 / pucc-turnin / part01 next >
Encoding:
Internet Message Format  |  1991-03-19  |  59.0 KB

  1. Subject:  v24i071:  Purdue tool for students to turn in work, Part01/02
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 2d802857 8018ea3f 676f3ed9 8a51e833
  5.  
  6. Submitted-by: Kevin Braunsdorf <ksb@cc.purdue.edu>
  7. Posting-number: Volume 24, Issue 71
  8. Archive-name: pucc-turnin/part01
  9.  
  10. This two part archive contains the PUCC electronic submissions system,
  11. it takes a while to install and RUNS SETUID ROOT.
  12.  
  13. Turnin(1L) and project(1L) provide administration of electronic
  14. submissions for a large number of classes.  These tools may be applied
  15. to any type of course (programming, art & design, word processing, etc.)
  16. which requires `students' to submit `projects' to an `instructor'.
  17.  
  18. A `student' is any login name on a UNIX system.  Multiple logins under
  19. the same uid are handled correctly.  The environment variables USER and
  20. LOGNAME are consulted (in that order) to determine which login name is
  21. submitting the file.
  22.  
  23. A `project' is a broad term for a group of submissions, one per login
  24. name, which will be collected over time.  These may then be processed
  25. by running a shell command for each project.   Submitted files are
  26. stored in a tar(1) format archive.  Large archives are kept compressed,
  27. but nether user nor administrator will see this detail.
  28.  
  29. An `instructor' is a supervising login name.  This login is able to
  30. limit the collection time for any `project' and issue grade commands.
  31.  
  32. The user interface to turnin(1L) is quite simple and obvious.  The user
  33. simply runs
  34.     $ turnin files
  35. to submit files.  If the request is ambiguous turnin will request
  36. information from stdin.
  37.  
  38.  
  39. #!/bin/sh
  40. # This is pucc-1e, a shell archive (produced by shar 3.49)
  41. # To extract the files from this archive, save it to a file, remove
  42. # everything above the "!/bin/sh" line above, and type "sh file_name".
  43. #
  44. # made 11/29/1990 16:01 UTC by ksb@cc.purdue.edu (Kevin Braunsdorf)
  45. # Source directory /ksb/c.s.u-2
  46. #
  47. # existing files will NOT be overwritten unless -c is specified
  48. #
  49. # This shar contains:
  50. # length  mode       name
  51. # ------ ---------- ------------------------------------------
  52. #   1608 -rw-r--r-- INSTALL.05
  53. #  30395 -r--r--r-- turnin/turnin.c
  54. #   5973 -r--r--r-- project/project.1l
  55. #   3242 -r--r--r-- turnin/INSTALL
  56. #   2553 -r--r--r-- turnin/turnin.1l
  57. #   2655 -r--r--r-- turnin.cf/turnin.5l
  58. #   1781 -r--r--r-- project/template
  59. #   2259 -rw-r--r-- project/machine.h
  60. #   1368 -rw-r--r-- turnin/Makefile
  61. #   1386 -rw-r--r-- turnin.cf/turnin.cf
  62. #   1361 -r--r--r-- turnin.cf/turnin.cf.5l
  63. #   1575 -r--r--r-- project/install
  64. #   1326 -rw-r--r-- project/Makefile
  65. #    767 -rw-r--r-- turnin.cf/Makefile
  66. #    366 -rw-r--r-- turnin.cf/ckcf.sh
  67. #    149 -r--r--r-- turnin.cf/Distfile
  68. #  32572 -r--r--r-- project/project.c
  69. #
  70. # ============= INSTALL.05 ==============
  71. if test -f 'INSTALL.05' -a X"$1" != X"-c"; then
  72.     echo 'x - skipping INSTALL.05 (File already exists)'
  73. else
  74. echo 'x - extracting INSTALL.05 (Text)'
  75. sed 's/^X//' << 'Purdue' > 'INSTALL.05' &&
  76. Contains:
  77. X    project(1l)    - control electronic submission
  78. X    turnin(1l)    - electronically submit files for grading
  79. X    turnin(5l)    - instructions for instructors who use turnin
  80. X    turnin.cf(5l)    - turnin configuration file format
  81. X
  82. X
  83. Notes on depends:
  84. X    - libopt.a need not be installed, but it would make life easier
  85. X      if you want other PUCC tools.
  86. X
  87. X    - turnin needs to be compiled with libopt.a
  88. X
  89. X    - project needs libopt.a and flock(2)   !!  BSD only, sorry  !!
  90. X
  91. X
  92. To install these tools:
  93. X
  94. 0\ read the manual pages, see if you want any of them
  95. X
  96. 1\ decide where to install all this stuff, change the destinations in
  97. X   the Makefiles {BIN,LIB,ETC,HEADER}
  98. X    vi */Makefile
  99. X    
  100. 2\ select a place for turnin's database file (now /usr/local/lib/turnin.cf)
  101. X    vi turnin.cf/Makefile project/machine.h
  102. X
  103. 3\ if your system doesn't have strcasecmp in libc you'll have to copy it
  104. X   from the mkcat distribution and add it to ONEC in the Makefiles
  105. X    
  106. 3\ build project & turnin
  107. X    (cd project && make)
  108. X    (cd turnin && make)
  109. X
  110. 4\ install the tools and files
  111. X    su
  112. X    (cd project && make install)            #
  113. X    (cd turnin && make install)            #
  114. X    (cd turnin.cf && make install)            #
  115. X    exit                        # the root shell
  116. X
  117. 5\ install the manual pages, turnin/turnin.5l will need lots of site editing
  118. X   (don't install it if you have not edited it)
  119. X    mkcat -v turnin/turnin.1l project/project.1l turnin.cf/turnin.cf.5l
  120. X
  121. 6\ clean up the dirs
  122. X    (cd turnin && make clean)
  123. X    (cd project && make clean)
  124. X
  125. 7\ add the mail-alias `enroll-info' to point to the turnin.cf maintainer
  126. X    : 'I do not know how *you* do that!'
  127. X
  128. kayessbee
  129. --
  130. Kevin Braunsdorf, ksb@cc.purdue.edu, pur-ee!ksb, purdue!ksb
  131. Purdue
  132. chmod 0644 INSTALL.05 ||
  133. echo 'restore of INSTALL.05 failed'
  134. Wc_c="`wc -c < 'INSTALL.05'`"
  135. test 1608 -eq "$Wc_c" ||
  136.     echo 'INSTALL.05: original size 1608, current size' "$Wc_c"
  137. fi
  138. # ============= turnin/turnin.c ==============
  139. if test ! -d 'turnin'; then
  140.     echo 'x - creating directory turnin'
  141.     mkdir 'turnin'
  142. fi
  143. if test -f 'turnin/turnin.c' -a X"$1" != X"-c"; then
  144.     echo 'x - skipping turnin/turnin.c (File already exists)'
  145. else
  146. echo 'x - extracting turnin/turnin.c (Text)'
  147. sed 's/^X//' << 'Purdue' > 'turnin/turnin.c' &&
  148. /*
  149. X * turnin files - electronic submission, the send side            (ksb)
  150. X *
  151. X * expects the environment varaiable
  152. X *    <SUBPREFIX><course>=d<div>s<sec>
  153. X * to be in the environment
  154. X *
  155. X * $Compile: ${cc-cc} ${cc_debug--O} -I/usr/include/local %f -o %F -lopt
  156. X *
  157. X * configuration
  158. X *    apcCourse contains a list of the courses, and their submit
  159. X *    directories, each of these directories contains a file called
  160. X *    "Projlist" which contains the current project numbers.
  161. X *    The students files will be filed in his division.section
  162. X *    subdirectory under his login name.
  163. X        THIS PROGRAM RUNS SET UID ROOT
  164. X */
  165. #include <sys/types.h>
  166. #include <sys/stat.h>
  167. #include <sys/file.h>
  168. #include <sys/wait.h>
  169. #include <sys/param.h>
  170. #include <errno.h>
  171. #include <pwd.h>
  172. #include <grp.h>
  173. #include <stdio.h>
  174. #include <ctype.h>        /* for isspace() and isdigit() macros    */
  175. #include <strings.h>
  176. #include "getopt.h"
  177. #include "../project/machine.h"
  178. X
  179. static char RCSId[] =
  180. X    "$Id: turnin.c,v 4.8 90/11/23 14:05:25 becker Exp $";
  181. X
  182. char *progname =        /* our name to the world        */
  183. X    RCSId;
  184. static int
  185. X    fVerbose = 0;        /* tar tvf %s for the user        */
  186. X
  187. typedef enum {
  188. X    INITUSER,        /* initialize a users account        */
  189. X    LISTPROJ,        /* only list projects for a course    */
  190. X    LISTSUB,        /* only list subscribed courses        */
  191. X    VERSION,        /* give version of the program        */
  192. X    UNKNOWN            /* nothing to do, maybe list tar file    */
  193. } WHAT;
  194. X
  195. static WHAT eWhat;        /* which operation the user wants    */
  196. X
  197. extern struct group *getgrnam(), *getgrgid();
  198. extern struct passwd *getpwnam();
  199. extern char *strchr(), *strrchr(), *getenv();
  200. extern char *mktemp(), **environ, *malloc(), *calloc();
  201. #define strsave(Mpch) strcpy(malloc(strlen(Mpch)+1), Mpch)
  202. extern int errno;
  203. extern char *sys_errlist[];
  204. #define strerror(Me)    (sys_errlist[Me])
  205. X
  206. static char acCompress[] =    /* path to compress            */
  207. X    COMPRESS_PATH;
  208. static char acDotZ[] =        /* compress extender            */
  209. X    ".Z";
  210. static char acTar[] =        /* path to tar                */
  211. X    TAR_PATH;
  212. static char acSub[] =        /* record the SUB_ prefix        */
  213. X    SUBPREFIX;
  214. static char acSectIgnore[] =    /* bogus, ``no sections'' string    */
  215. X    SECTIGNORE;
  216. static int iCourses;        /* number of valid subscribed courses    */
  217. X
  218. static char *apcCourse[MAXCOURSE];    /* who                */
  219. static char *apcUid[MAXCOURSE];        /* who -> uid            */
  220. static char *apcDir[MAXCOURSE];        /* who -> where            */
  221. static char *apcGroup[MAXCOURSE];    /* who -> group            */
  222. static char *apcSections[MAXCOURSE];    /* who -> sections (*)        */
  223. X
  224. /* lines from the project file    */
  225. static char *apcProjects[MAXPROJECTS+1];
  226. X
  227. /*
  228. X * reads a configuration file of the form:            (doc/ksb)
  229. X * course:alpha uid:subdir for this course:alpha gid\n
  230. X */
  231. static void
  232. ReadDB()
  233. {
  234. X    register FILE *fpDB;
  235. X    register char *pc;
  236. X    register int i;
  237. X    static char acError[] = "%s: error in data base line %d\n";
  238. X    static char acLine[MAXCHARS];
  239. X    static char acTurnbase[] = TURNBASE; 
  240. X
  241. X    if (0 == (fpDB = fopen(acTurnbase, "r"))) {
  242. X        fprintf(stderr, "%s: fopen: %s: %s\n", progname, acTurnbase, strerror(errno));
  243. X        exit(2);
  244. X    }
  245. X
  246. X    i = 0;
  247. X    while ((char *)0 != fgets(acLine, MAXCHARS, fpDB)) {
  248. X        pc = acLine;
  249. X        while (isspace(*pc) && '\n' != *pc)
  250. X            ++pc;
  251. X        if ('#' == *pc || '\n' == *pc)    /* comments and blank lines */
  252. X            continue;
  253. X        apcCourse[i] = pc = strsave(pc);
  254. X        if ((char *)0 == (pc = strchr(pc, SEP))) {
  255. X            printf(acError, progname, i);
  256. X            exit(16);
  257. X        }
  258. X        *pc++ = '\000';
  259. X        apcUid[i] = pc;
  260. X        if ((char *)0 == (pc = strchr(pc, SEP))) {
  261. X            printf(acError, progname, i);
  262. X            exit(17);
  263. X        }
  264. X        *pc++ = '\000';
  265. X        apcDir[i] = pc;
  266. X        if ((char *)0 == (pc = strchr(pc, SEP))) {
  267. X            printf(acError, progname, i);
  268. X            exit(18);
  269. X        }
  270. X        *pc++ = '\000';
  271. X        apcGroup[i] = pc;
  272. X        if ((char *)0 == (pc = strchr(pc, SEP))) {
  273. X            printf(acError, progname, i);
  274. X            exit(19);
  275. X        }
  276. X        *pc++ = '\000';
  277. X        apcSections[i] = pc;
  278. X        if ((char *)0 != (pc = strchr(pc, '\n'))) {
  279. X            *pc = '\000';
  280. X        }
  281. X        ++i;
  282. X    }
  283. X    apcCourse[i] = (char *)0;
  284. }
  285. X
  286. /*
  287. X * list all the courses turnin knows about                (ksb)
  288. X */
  289. void
  290. ListCourses(fp)
  291. FILE *fp;
  292. {
  293. X    register int iWho;
  294. X
  295. X    for (iWho = 0; (char *)0 != apcCourse[iWho]; ++iWho) {
  296. X        fprintf(fp, "%16s", apcCourse[iWho]);
  297. X        if (3 == iWho % 4)
  298. X            fprintf(fp, "\n");
  299. X    }
  300. X    if (0 != iWho % 4) {
  301. X        fprintf(fp, "\n");
  302. X    }
  303. }
  304. X
  305. /*
  306. X * list the sections in the given course                (ksb)
  307. X * input which one the user wants and filter it for case
  308. X */
  309. void
  310. FindSection(iWho, fp, pcIndex)
  311. int iWho;
  312. FILE *fp;
  313. char *pcIndex;
  314. {
  315. X    register char *pcComma, *pcLast;
  316. X
  317. X    /* only one, take that one
  318. X     */
  319. X    if ((char *)0 == strchr(apcSections[iWho], ',')) {
  320. X        /** fprintf(fp, "There is only one section for %s, using \"%s\".\n", apcCourse[iWho], apcSections[iWho]);
  321. X         **/
  322. X        (void)strcpy(pcIndex, apcSections[iWho]);
  323. X        return;
  324. X    }
  325. X    /* if the user gave us one to try
  326. X     */
  327. X    if ('\000' != *pcIndex) {
  328. X        goto trysec;
  329. X    }
  330. X    for (;;) {
  331. X        fprintf(fp, "The sections of %s are:\n", apcCourse[iWho]);
  332. X        pcComma = apcSections[iWho];
  333. X        while ((char *)0 != (pcLast = pcComma)) {
  334. X            pcComma = strchr(pcComma, ',');
  335. X            if ((char *)0 != pcComma)
  336. X                *pcComma = '\000';
  337. X            fprintf(fp, "\t%s\n", pcLast);
  338. X            if ((char *)0 != pcComma)
  339. X                *pcComma++ = ',';
  340. X        }
  341. X
  342. X        fprintf(fp, "Enter your section: ");
  343. X        if ((char *)0  == fgets(pcIndex, MAXDIVSEC, stdin)) {
  344. X            fprintf(fp, "quit\n");
  345. X            exit(0);
  346. X        }
  347. X        if ((char *)0 == (pcLast = strchr(pcIndex, '\n'))) {
  348. X            fprintf(fp, "%s: invalid division/section identifier\n", progname);
  349. X            continue;
  350. X        }
  351. X        *pcLast = '\000';
  352. X
  353. X    trysec:
  354. X        pcComma = apcSections[iWho];
  355. X        while ((char *)0 != (pcLast = pcComma)) {
  356. X            pcComma = strchr(pcComma, ',');
  357. X            if ((char *)0 != pcComma)
  358. X                *pcComma = '\000';
  359. X            if (0 == strcasecmp(pcIndex, pcLast)) {
  360. X                /* fix the case for them */
  361. X                (void)strcpy(pcIndex, pcLast);
  362. X                if ((char *)0 != pcComma)
  363. X                    *pcComma++ = ',';
  364. X                return;
  365. X            }
  366. X            if ((char *)0 != pcComma)
  367. X                *pcComma++ = ',';
  368. X        }
  369. X        fprintf(fp, "%s: cannot find section `%s\' in the list for `%s\', retype it please.\n", progname, pcIndex, apcCourse[iWho]);
  370. X    }
  371. }
  372. X
  373. /*
  374. X * If the given course if on the course list, return its index        (aho)
  375. X * in the apcCourse[] array.  Otherwise, return -1.
  376. X */
  377. static int
  378. VerifyCourse(pcCourse)
  379. char *pcCourse;
  380. {
  381. X    register char **ppch;
  382. X
  383. X    for (ppch = apcCourse; (char *)0 != *ppch; ++ppch) {
  384. X        if (0 == strcasecmp(pcCourse, *ppch)) {
  385. X            return ppch - apcCourse;
  386. X        }
  387. X    }
  388. X    return -1;
  389. }
  390. X
  391. X
  392. /*
  393. X * we need to tell the user (on stdout) what to put in his        (ksb)
  394. X * .login/.profile.  Write to him only on stderr.
  395. X * (run as user for no reason, btw)
  396. X */
  397. int
  398. DoInit(argc, argv, pcGuess)
  399. int argc;
  400. char **argv, *pcGuess;
  401. {
  402. X    register char *pcIndex, *pcEqual;
  403. X    auto int iWho, fShell, fMore;
  404. X    auto char acBuf[MAXCOURSENAME + MAXDIVSEC];
  405. X    auto char *pcShell, acYesNo[MAXYESNO+1];
  406. X    auto char *pcKopOut = "abort";
  407. X
  408. X    fprintf(stderr, "\n\
  409. \tConfiguring your account to send electronic submissions\n\n\
  410. This program sets up your computer account so that you may use \"%s\",\n\
  411. the electronic submission program.\n\n", progname);
  412. X
  413. X    if ((char *)0 == (pcShell = getenv("SHELL"))) {
  414. X        pcShell = "/bin/sh";
  415. X    }
  416. X
  417. X    iWho = strlen(pcShell);
  418. X    if (iWho >= 3 && 'c' == pcShell[iWho-3])
  419. X        fShell = 'c';
  420. X    else
  421. X        fShell = 's';
  422. X
  423. X    /* if the user used turnin -i -c cs100 we need to put cs100 in the list
  424. X     * we know argv is (char *)0 terminated, we can use that slot.
  425. X     */
  426. X    if ((char *)0 != pcGuess) {
  427. X        argv[argc] = pcGuess;
  428. X        ++argc;
  429. X    }
  430. X    fMore = 0 == argc;
  431. X    if (fMore) {
  432. X        fprintf(stderr, "\
  433. Below you will see a list of courses that are presently using this system.\n\
  434. For each course that you are enrolled in, enter its name and section when\n\
  435. prompted.  When you have entered information for all of your courses, press\n\
  436. ^D (Control-D) to quit.\n");
  437. X    } else {
  438. X        fprintf(stderr, "\
  439. By responding the prompts below you will be enrolled in electronic\n\
  440. submissions for");
  441. X        for (iWho = 0; iWho < argc; ++iWho) {
  442. X            fprintf(stderr, "%s%s", (0 == iWho ? " " : ", "), argv[iWho]);
  443. X        }
  444. X        fprintf(stderr, ".\n");
  445. X    }
  446. X    while (fMore || argc > 0) {
  447. X        fprintf(stderr, "\n");
  448. X        if ((char *)0 != argv[0]) {
  449. X            (void)strcpy(acBuf, argv[0]);
  450. X            pcIndex = strchr(acBuf, '\000');
  451. X            --argc, ++argv;
  452. X            goto tryit;
  453. X        }
  454. X
  455. X        fprintf(stderr, "Here is a list of the courses currently available\n");
  456. X        fprintf(stderr, "(if you cannot find your course in this list type it anyway):\n");
  457. X        for (;;) {
  458. X            ListCourses(stderr);
  459. X            fprintf(stderr, "Enter a course name you are subscribed to or ^D to quit: ");
  460. X            fflush(stderr);
  461. X            if ((char *)0 == fgets(acBuf, MAXCOURSENAME, stdin) || 0 == strcmp("^D\n", acBuf) || 0 == strcmp("^d\n", acBuf)) {
  462. X                fprintf(stderr, "%s\n", pcKopOut);
  463. X                exit(0);
  464. X            }
  465. X            if ((char *)0 == (pcIndex = strchr(acBuf, '\n'))) {
  466. X                fprintf(stderr, "%s: invalid course name\n", progname);
  467. X                exit(6);
  468. X            }
  469. X            *pcIndex = '\000';
  470. X    tryit:
  471. X            if ('\000' == acBuf[0]) {
  472. X                /* skip blank lines */
  473. X                continue;
  474. X            }
  475. X            if ((char *)0 != (pcEqual = strchr(acBuf, '='))) {
  476. X                pcIndex = pcEqual;
  477. X                *pcIndex = '\000';
  478. X            }
  479. X            if (-1 != (iWho = VerifyCourse(acBuf))) {
  480. X                break;
  481. X            }
  482. X            fprintf(stderr, "\nIt is possible that you have entered a course name that will be valid later\n");
  483. X            fprintf(stderr, "If you are sure that is the name of your course I will arrange to have %s\n", progname);
  484. X            fprintf(stderr, "ask you for the section information later.\n\n");
  485. X            fprintf(stderr, "Is `%s\' is a course identifier for one of your courses? [ny] ", acBuf);
  486. X            if ((char *)0 == fgets(acYesNo, MAXYESNO, stdin)) {
  487. X                exit(1);
  488. X            }
  489. X            if ('y' == acYesNo[0] || 'Y' == acYesNo[0]) {
  490. X                if ((char *)0 == pcEqual) {
  491. X                    *pcIndex++ = '\000';
  492. X                    *pcIndex = '\000';
  493. X                }
  494. X                goto unknown;
  495. X            }
  496. X        }
  497. X
  498. X        /*
  499. X         * if the user is subscribed to this course, don't ask him
  500. X         * for divsion/section
  501. X         */
  502. X        *pcIndex++ = '\000';
  503. X        if ((char *)0 == pcEqual) {
  504. X            *pcIndex = '\000';
  505. X        }
  506. X
  507. X        FindSection(iWho, stderr, pcIndex);
  508. X
  509. X        (void)setpwent();
  510. X        if ((struct passwd *)0 == getpwnam(apcUid[iWho])) {
  511. X            fprintf(stderr, "Instructor \"%s\" doesn't exist on this machine, therefore, you probably\n", apcUid[iWho]);
  512. X            fprintf(stderr, "cannot use this machine for \"%s\".\n", acBuf);
  513. X        }
  514. X        (void)endpwent();
  515. X
  516. X        (void)setgrent();
  517. X        if ((struct group *)0 == getgrnam(apcGroup[iWho])) {
  518. X            fprintf(stderr, "Group \"%s\" doesn't exist on this machine, report this to a consultant.\n", apcUid[iWho]);
  519. X        }
  520. X        (void)endgrent();
  521. X
  522. X    unknown:
  523. X        if ('s' == fShell) {
  524. X            printf("%s%s=\'%s\'; export %s%s\n", acSub, acBuf, pcIndex, acSub, acBuf);
  525. X        } else {
  526. X            printf("setenv %s%s \'%s\'\n",acSub, acBuf, pcIndex);
  527. X        }
  528. X        pcKopOut = "done";
  529. X    }
  530. }
  531. X
  532. /*
  533. X * compare two strings for qsort
  534. X */
  535. static int
  536. q_compare(ppch1, ppch2)
  537. X    char **ppch1, **ppch2;
  538. {
  539. X    return strcmp(*ppch1, *ppch2);
  540. }
  541. X
  542. X
  543. /*
  544. X * Move environment variables beginning with SUBPREFIX to the        (aho)
  545. X * beginning of the environment (using swaps).  Sort the
  546. X * SUBPREFIX variables. Set the global variable iCourses to
  547. X * the number of submit variables found.
  548. X * If we find a SUBPREFIX variable in the environment for a course we
  549. X * don't find on the course list, we throw it out.
  550. X *
  551. X * for SUBPREFIX="SUB_"
  552. X *    SUB_cs300=d1s1
  553. X *    SUB_cs400=morning
  554. X *    SUB_cs400=930kevin
  555. X *
  556. X * note: we assume we can write on what environ points to
  557. X */
  558. static int
  559. FixEnvironment()
  560. {
  561. X    register char **ppch;
  562. X    register char *pcTemp;
  563. X    register char *pcIndex;
  564. X
  565. X    iCourses = 0;
  566. X    for (ppch = environ; (char *)0 != *ppch; ++ppch) {
  567. X        if (0 == strncmp(acSub, *ppch, sizeof(acSub)-1) &&
  568. X        (char *)0 != (pcIndex = strchr(sizeof(acSub)-1 + *ppch, '='))) {
  569. X            *pcIndex = '\000';
  570. X            if (-1 != VerifyCourse(sizeof(acSub)-1 + *ppch)) {
  571. X                pcTemp = environ[iCourses];
  572. X                environ[iCourses] = *ppch;
  573. X                *ppch = pcTemp;
  574. X                ++iCourses;
  575. X            }
  576. X            *pcIndex = '=';
  577. X        }
  578. X    }
  579. X    if (1 < iCourses) {
  580. X        qsort((char *)environ, iCourses, sizeof(char *), q_compare);
  581. X    }
  582. }
  583. X
  584. X
  585. /*
  586. X * Search the subscribed courses (the SUB_<course> environment         (ksb/aho)
  587. X * varibles) for a single course this submission is to.  If
  588. X * there is more than one subscribed course, use pcGuess to
  589. X * arbitrate.  If we fail, return (char *)0.
  590. X *
  591. X * note: we assume that FixEnvironment() has been called before us
  592. X */
  593. static char *
  594. Which(pcGuess)
  595. char *pcGuess;
  596. {
  597. X    register char **ppch;
  598. X    register char *pcCourse;
  599. X    register int i;
  600. X
  601. X    pcCourse = (char *)0;
  602. X    for (ppch = environ; ppch < & environ[iCourses]; ++ppch) {
  603. X        /* two course in env, no -c, use Ask from main
  604. X         */
  605. X        if ((char *)0 != pcCourse) {
  606. X            return (char *)0;
  607. X        }
  608. X        pcCourse = sizeof(acSub)-1 + *ppch;
  609. X        if ((char *)0 == pcGuess) {
  610. X            continue;
  611. X        }
  612. X        i = strlen(pcGuess);
  613. X        if (0 == strncmp(pcGuess, pcCourse, i) && '=' == pcCourse[i]) {
  614. X            return pcCourse;
  615. X        }
  616. X        pcCourse = (char *)0;
  617. X    }
  618. X    return pcCourse;
  619. }
  620. X
  621. /*
  622. X * List the courses the user is subscribed to which are also in the
  623. X * course list.
  624. X */
  625. static void
  626. ListSubscribed(fp)
  627. FILE *fp;
  628. {
  629. X    register char **ppchEnv;
  630. X    register char *pc;
  631. X    register int chSep = ':';
  632. X
  633. X    if (0 == iCourses) {
  634. X        fprintf(fp, "You are not subscribed to any courses.\n");
  635. X        return;
  636. X    }
  637. X    fprintf(fp, "You are subscribed to");
  638. X    for (ppchEnv = environ; ppchEnv < & environ[iCourses]; ++ppchEnv) {
  639. X        if ((char *)0 == (pc = strchr(sizeof(acSub)-1 + *ppchEnv, '='))) {
  640. X            continue;
  641. X        }
  642. X        *pc = '\000';
  643. X        fprintf(fp, "%c %s", chSep, sizeof(acSub)-1 + *ppchEnv);
  644. X        *pc = '=';
  645. X        chSep = ',';
  646. X    }
  647. X    putc('\n', fp);
  648. }
  649. X
  650. /*
  651. X * get the words of wisdom from the user                (ksb)
  652. X * strip leading white space, etc
  653. X */
  654. char *
  655. GetWord(pcBuf, iLen, pcDef)
  656. char *pcBuf, *pcDef;
  657. int iLen;
  658. {
  659. X    register char *pcWhite, *pcCopy;
  660. X    register int l;
  661. X
  662. X    fflush(stdout);
  663. X    pcBuf[0] = '\n';
  664. X    if ((char *)0 == fgets(pcBuf, iLen, stdin))
  665. X        return (char *)0;
  666. X    pcWhite = pcBuf;
  667. X    while (isspace(*pcWhite) && '\n' != *pcWhite)
  668. X        ++pcWhite;
  669. X
  670. X    pcCopy = pcBuf;
  671. X    l = iLen;
  672. X    while (l-- > 0 && '\n' != (*pcCopy = *pcWhite))
  673. X        ++pcCopy, ++pcWhite;
  674. X
  675. X    if (pcCopy == pcBuf)
  676. X        (void)strncpy(pcBuf, pcDef, iLen);
  677. X    else
  678. X        *pcCopy = '\000';
  679. X    return pcBuf;
  680. }
  681. X
  682. /*
  683. X * We couldn't figure out the course, so now we ask the user        (aho)
  684. X * interactively.  We return the course and div/sec as
  685. X * follows: cs100=d1s1
  686. X *
  687. X * invariant: we do not return (char *)0
  688. X */
  689. char *
  690. AskCourse()
  691. {
  692. X    register char **ppch;
  693. X    auto struct group *grpMe;
  694. X    static char acBuf[MAXCOURSENAME + MAXDIVSEC];
  695. X    static char acDef[MAXCOURSENAME + MAXDIVSEC];
  696. X
  697. X    /* if our groupname is a valid course, use it as the default
  698. X     * else if our groupname is the group for a course, use that
  699. X     */
  700. X    acDef[0] = '\000';
  701. X    (void)setgrent();
  702. X    if ((struct group *)0 != (grpMe = getgrgid(getgid()))) {
  703. X        (void)strcpy(acDef, grpMe->gr_name);
  704. X        if (-1 == VerifyCourse(acDef)) {
  705. X            register int i;
  706. X            for (i = 0; (char *)0 != apcCourse[i]; ++i) {
  707. X                if (0 == strcmp(apcGroup[i], acDef))
  708. X                    break;
  709. X            }
  710. X            if ((char *)0 != apcCourse[i]) {
  711. X                (void)strcpy(acDef, apcCourse[i]);
  712. X            } else {
  713. X                acDef[0] = '\000';
  714. X            }
  715. X        }
  716. X    }
  717. X    (void)endgrent();
  718. X
  719. X    switch (iCourses) {
  720. X    case 0:
  721. X        printf("\
  722. You are not suscribed to any valid course, here is a list of the courses\n\
  723. you may use currently:\n");
  724. X        ListCourses(stdout);
  725. X        /* use the first course as the default
  726. X         */
  727. X        if ('\000' == acDef[0] && (char *)0 != apcCourse[0]) {
  728. X            (void)strcpy(acDef, apcCourse[0]);
  729. X        }
  730. X        break;
  731. X    default:
  732. X        ListSubscribed(stdout);
  733. X        /* use the first subscribed course as the default
  734. X         */
  735. X        if ('\000' == acDef[0]) {
  736. X            register int i;
  737. X            register char *pcIndex;
  738. X
  739. X            pcIndex = environ[0] + sizeof(acSub)-1;
  740. X            for (i = 0; '=' != pcIndex[i]; ++i) {
  741. X                acDef[i] = pcIndex[i];
  742. X            }
  743. X            acDef[i] = '\000';
  744. X        }
  745. X        printf("\nIf the course you wish to submit to is not on the above list, try it anyway\n");
  746. X        break;
  747. X    }
  748. X    printf("Enter course name? [%s] ", acDef);
  749. X
  750. X    if ((char *)0 == GetWord(acBuf, MAXCOURSENAME, acDef)) {
  751. X        fprintf(stderr, "%s: invalid course name\n", progname);
  752. X        exit(6);
  753. X    }
  754. X    if (-1 == VerifyCourse(acBuf)) {
  755. X        fprintf(stderr, "%s: cannot find \"%s\" in the course list\n", progname, acBuf);
  756. X        exit(7);
  757. X    }
  758. X    /*
  759. X     * if the user is subscribed to this course, don't ask him
  760. X     * for divsion/section, return the info here
  761. X     */
  762. X    for (ppch = environ; ppch < & environ[iCourses]; ++ppch) {
  763. X        if (0 == strncmp(acBuf, sizeof(acSub)-1 + *ppch, strlen(acBuf))) {
  764. X            return sizeof(acSub)-1 + *ppch;
  765. X        }
  766. X    }
  767. X    return acBuf;
  768. }
  769. X
  770. /*
  771. X * read the given project list file and store in apcProjects        (aho)
  772. X */
  773. ReadProjects(pcFile)
  774. char *pcFile;
  775. {
  776. X    register FILE *fp;
  777. X    register char *pc;
  778. X    register int iProject;
  779. X    auto char acLine[MAXCHARS];
  780. X
  781. X    iProject = 0;
  782. X    if ((FILE *)0 == (fp = fopen(pcFile, "r"))) {
  783. X        printf("%s: can\'t find project list file in %s\n", progname, pcFile);
  784. X        exit(9);
  785. X    }
  786. X    while ((char *)0 != fgets(acLine, MAXCHARS, fp)) {
  787. X        if ('\n' == acLine[0] || '#' == acLine[0])
  788. X            continue;
  789. X        if (iProject >= MAXPROJECTS) {
  790. X            /*ZZZ bogus */
  791. X            fprintf(stderr, "%s: too many projects in project file\n", progname);
  792. X            exit(10);
  793. X        }
  794. X        pc = acLine + strlen(acLine) - 1;
  795. X        while (isspace(*pc)) {
  796. X            *pc-- = '\000';
  797. X        }
  798. X        apcProjects[iProject++] = strsave(acLine);
  799. X    }
  800. X    apcProjects[iProject] = (char *)0;
  801. X    (void)fclose(fp);
  802. }
  803. X
  804. /*
  805. X * Determine the project name for this submission.  If we can't figure it out,
  806. X * we print errors and exit.
  807. X */
  808. static char *
  809. ProjName(pcGuess, pcClass)
  810. X    char *pcGuess, *pcClass;
  811. {
  812. X    register char **ppch;
  813. X
  814. X    for (ppch = apcProjects; (char *)0 != *ppch; ++ppch) {
  815. X        switch (**ppch) {
  816. X        case '=':
  817. X            if ((char *)0 == pcGuess) {
  818. X                return 1 + *ppch;
  819. X            }
  820. X            /*FALLTHROUGH*/
  821. X        case '+':
  822. X            if ((char *)0 == pcGuess) {
  823. X                continue;
  824. X            }
  825. X            if (0 == strcasecmp(1+*ppch, pcGuess)) {
  826. X                return 1 + *ppch;
  827. X            }
  828. X            break;
  829. X        default:
  830. X            if ((char *)0 == pcGuess) {
  831. X                continue;
  832. X            }
  833. X            if (0 == strcasecmp(1+*ppch, pcGuess)) {
  834. X                fprintf(stderr, "%s: submissions for ", progname);
  835. X                if (isdigit(**ppch)) {
  836. X                    fprintf(stderr, "project ");
  837. X                }
  838. X                fprintf(stderr, "%s have been turned off.\n", *ppch);
  839. X                exit(11);
  840. X            }
  841. X        }
  842. X    }
  843. X    if ((char *)0 == pcGuess) {
  844. X        fprintf(stderr, "%s: no current project for %s\n", progname, pcClass);
  845. X        fprintf(stderr, "Use \"%s -l\" for a list of projects in a course.\n", progname);
  846. X        exit(12);
  847. X    }
  848. X    fprintf(stderr, "%s: ", progname);
  849. X    if (isdigit(**ppch)) {
  850. X        fprintf(stderr, "Project ");
  851. X    }
  852. X    fprintf(stderr, "\"%s\" is not a current project for submission in %s.  Use\n\"project -l\" for a list of projects in this course.\n", pcGuess, pcClass);
  853. X    exit(13);
  854. X    /*NOTREACHED*/
  855. }
  856. X
  857. X
  858. /*
  859. X * list the valid projects to stdout; the argument pcClass is for
  860. X * printing headers/error messages
  861. X *
  862. X * (assume that ReadProjects has been called)
  863. X */
  864. static void
  865. ListProjects(pcCourse)
  866. char *pcCourse;
  867. {
  868. X    register char **ppch;
  869. X
  870. X    if ((char *)0 == apcProjects[0]) {
  871. X        printf("There are no current projects for %s\n", pcCourse);
  872. X        return;
  873. X    }
  874. X    printf("Current projects for %s:\n", pcCourse);
  875. X    for (ppch = apcProjects; (char *)0 != *ppch; ++ppch) {
  876. X        printf("%-16s", 1+*ppch);
  877. X        switch (**ppch) {
  878. X        case '=':
  879. X            printf(" on (current)\n");
  880. X            break;
  881. X        case '+':
  882. X            printf(" on (alternate)\n");
  883. X            break;
  884. X        default:
  885. X            printf(" off\n");
  886. X            break;
  887. X        }
  888. X    }
  889. }
  890. X
  891. X
  892. /*
  893. X * To prevent people from making clever paths to the turnin dirctory
  894. X * with empty components, dots, and slashes, call this routine
  895. X */
  896. static void
  897. GoodFile(pcName, pcWhat)
  898. char *pcName, *pcWhat;
  899. {
  900. X    if ((char *)0 == pcName || '\000' == pcName[0]) {
  901. X        fprintf(stderr, "%s: no %s specified\n", progname, pcWhat);
  902. X        exit(13);
  903. X    }
  904. X    if ((char *)0 != strchr(pcName, '.')) {
  905. X        fprintf(stderr, "%s: will not allow %ss with \'.\' in them\n", progname, pcWhat);
  906. X        exit(14);
  907. X    }
  908. X    if ((char *)0 != strchr(pcName, '/')) {
  909. X        fprintf(stderr, "%s: will not allow %ss with \'/\' in them\n", progname, pcWhat);
  910. X        exit(15);
  911. X    }
  912. }
  913. X
  914. /*
  915. X * show an index of the submitted files                    (ksb)
  916. X * prefer the uncompressed file
  917. X *    tar tvf - <file
  918. X *
  919. X * but show compressed if no other choice
  920. X *    compress -d <file | tar tvf -
  921. X */
  922. void
  923. ShowTar(pcFile)
  924. char *pcFile;
  925. {
  926. X    auto char *nargv[4];
  927. X    auto int pid, n;
  928. X    auto int fds[2];
  929. X
  930. X    fflush(stdout);
  931. X    fflush(stderr);
  932. X    switch (pid = fork()) {
  933. X    case 0:
  934. X        close(0);
  935. X        if (0 != open(pcFile, O_RDONLY, 0600)) {
  936. X            strcat(pcFile, acDotZ);
  937. X            if (0 != open(pcFile, O_RDONLY, 0600)) {
  938. X                fprintf(stderr, "%s: open: %s: %s\n", progname, pcFile, strerror(errno));
  939. X                exit(1);
  940. X            }
  941. X            if (0 != pipe(fds)) {
  942. X                fprintf(stderr, "%s: pipe: %s\n", progname, strerror(errno));
  943. X                exit(1);
  944. X            }
  945. X            switch (fork()) {
  946. X            case -1:
  947. X                fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
  948. X                exit(1);
  949. X            case 0:        /* zcat of tar file.Z        */
  950. X                close(1);
  951. X                dup(fds[1]);
  952. X                close(fds[1]);
  953. X                close(fds[0]);
  954. X                nargv[0] = "compress";
  955. X                nargv[1] = "-d";
  956. X                nargv[2] = (char *)0;
  957. X                execve(acCompress, nargv, environ);
  958. X                /* exec zcat */
  959. X                exit(1);
  960. X            default:    /* |tar fvf -            */
  961. X                close(0);
  962. X                dup(fds[0]);
  963. X                close(fds[0]);
  964. X                close(fds[1]);
  965. X                break;
  966. X            }
  967. X        }
  968. X        nargv[0] = "tar";
  969. X        nargv[1] = "-tvf";
  970. X        nargv[2] = "-";
  971. X        nargv[3] = (char *)0;
  972. X        execve(acTar, nargv, environ);
  973. X        fprintf(stderr, "%s: execve: %s: %s\n", progname, acTar, strerror(errno));
  974. X        exit(21);
  975. X        break;
  976. X    case -1:
  977. X        fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
  978. X        return;
  979. X    default:
  980. X        break;
  981. X    }
  982. X    while (-1 != (n = wait((union wait *)0)) && pid != n)
  983. X        ;
  984. }
  985. X
  986. static char acHelp[] =
  987. X    "%s: usage [-hlsvV] [-c course[=section]] [-p project] [files]\n";
  988. static char *apcHelp[] = {
  989. X    "c course   specify the course to use",
  990. X    "h       print this help message",
  991. X    "i       output shell commands to set user\'s environment",
  992. X    "l       list projects active for the selected course",
  993. X    "p project  specify the project to submit to",
  994. X    "s       list courses the envoker is enrolled in",
  995. X    "v       be verbose (also passed to tar)",
  996. X    "V       show version information",
  997. X    "files       list of files to submit",
  998. X    (char *)0
  999. };
  1000. X
  1001. /*
  1002. X * output a long usage message                        (ksb)
  1003. X */
  1004. void
  1005. usage(fp)
  1006. FILE *fp;
  1007. {
  1008. X    register char **ppc;
  1009. X
  1010. X    fprintf(fp, acHelp, progname);
  1011. X    for (ppc = apcHelp; (char *)0 != *ppc; ++ppc)
  1012. X        fprintf(fp, "%s\n", *ppc);
  1013. }
  1014. X
  1015. /*
  1016. X * make an archive and transfer to instructors account            (ksb)
  1017. X */
  1018. int
  1019. main(argc, argv)
  1020. int argc;
  1021. char **argv;
  1022. {
  1023. X    register int i, n, pid;
  1024. X    register char *pcCourse;
  1025. X    register char **nargv;
  1026. X    register char *pcProj;
  1027. X    register char *pcDiv;
  1028. X    auto char *pcUser;
  1029. X    auto struct passwd *ppw;
  1030. X    auto struct group *pgr;
  1031. X    static char acPath[MAXPATHLEN+1];
  1032. X    static char acYes[MAXCHARS];
  1033. X    static int iWho;
  1034. X    static struct stat stat_buf;
  1035. X    static union wait wait_buf;
  1036. X    static char acOnly[] = "%s: only one of -i, -l,or  -s may be given\n";
  1037. X
  1038. X    /* RUNS SET UID ROOT */
  1039. X
  1040. X    if ((char *)0 == (progname = strrchr(argv[0], '/')))
  1041. X        progname = argv[0];
  1042. X    else
  1043. X        ++progname;
  1044. X
  1045. X    /*
  1046. X     * command line parser
  1047. X     */
  1048. X    pcCourse = (char *)0;
  1049. X    pcProj = (char *)0;
  1050. X    eWhat = UNKNOWN;
  1051. X    while (EOF != (i = getopt(argc, argv, "hilsp:c:vV"))) {
  1052. X        switch(i) {
  1053. X        case 'h':
  1054. X            usage(stdout);
  1055. X            exit(0);
  1056. X        case 'i':
  1057. X            if (UNKNOWN != eWhat) {
  1058. X                fprintf(stderr, acOnly, progname);
  1059. X                exit(1);
  1060. X            }
  1061. X            eWhat = INITUSER;
  1062. X            break;
  1063. X        case 'l':
  1064. X            if (UNKNOWN != eWhat) {
  1065. X                fprintf(stderr, acOnly, progname);
  1066. X                exit(1);
  1067. X            }
  1068. X            eWhat = LISTPROJ;
  1069. X            break;
  1070. X        case 's':
  1071. X            if (UNKNOWN != eWhat) {
  1072. X                fprintf(stderr, acOnly, progname);
  1073. X                exit(1);
  1074. X            }
  1075. X            eWhat = LISTSUB;
  1076. X            break;
  1077. X        case 'V':
  1078. X            if (UNKNOWN != eWhat) {
  1079. X                fprintf(stderr, acOnly, progname);
  1080. X                exit(1);
  1081. X            }
  1082. X            eWhat = VERSION;
  1083. X            break;
  1084. X        case 'v':
  1085. X            fVerbose = 1;
  1086. X            break;
  1087. X        case 'p':
  1088. X            pcProj = optarg;
  1089. X            break;
  1090. X        case 'c':
  1091. X            pcCourse = optarg;
  1092. X            break;
  1093. X        default:
  1094. X            fprintf(stderr, acHelp, progname);
  1095. X            exit(1);
  1096. X            break;
  1097. X        }
  1098. X    }
  1099. X    if (optind == argc && UNKNOWN == eWhat && 0 == fVerbose) {
  1100. X        fprintf(stderr, acHelp, progname);
  1101. X        exit(1);
  1102. X    }
  1103. X
  1104. X    /*
  1105. X     * Read submission information from the user's environment, from
  1106. X     * the instructors project list file, and from our data base, and
  1107. X     * check it all for consistency.
  1108. X     */
  1109. X    ReadDB();
  1110. X    if (INITUSER == eWhat) {
  1111. #ifdef HP_UX
  1112. X        if (-1 == setresuid(-1, getuid(), -1)) {
  1113. X            fprintf(stderr, "%s: setresuid: %s\n", progname, strerror(errno));
  1114. #else
  1115. X        if (-1 == seteuid(getuid())) {
  1116. X            fprintf(stderr, "%s: seteuid: %s\n", progname, strerror(errno));
  1117. #endif
  1118. X            exit(20);
  1119. X        }
  1120. X        DoInit(argc-optind, argv+optind, pcCourse);
  1121. X        exit(0);
  1122. X    }
  1123. X    if (VERSION == eWhat) {
  1124. X        printf("%s: %s\n", progname, RCSId);
  1125. X        exit(0);
  1126. X    }
  1127. X
  1128. X    FixEnvironment();
  1129. X    if (LISTSUB == eWhat) {
  1130. X        if (0 == iCourses) {
  1131. X            printf("You are not subscribed to any courses.\n");
  1132. X            exit(1);
  1133. X        }
  1134. X        ListSubscribed(stdout);
  1135. X        exit(0);
  1136. X    }
  1137. X
  1138. X    if ((char *)0 == pcCourse && iCourses > 0) {
  1139. X        pcCourse = Which(pcCourse);
  1140. X    }
  1141. X    if ((char *)0 == pcCourse) {
  1142. X        printf("\n\
  1143. For this %s, you may enter the course information interactively.\n",  LISTPROJ == eWhat ? "listing" : "submission");
  1144. X        pcCourse = AskCourse();
  1145. X    }
  1146. X    if ((char *)0 != (pcDiv = strchr(pcCourse, '='))) {
  1147. X        *pcDiv++ = '\000';
  1148. X    }
  1149. X
  1150. X    if (-1 == (iWho = VerifyCourse(pcCourse))) {
  1151. X        fprintf(stderr, "%s: cannot find your course (%s) in course list\n", progname, pcCourse);
  1152. X        exit(7);
  1153. X    }
  1154. X
  1155. X    (void) setpwent();
  1156. X    if ((struct passwd *)0 == (ppw = getpwnam(apcUid[iWho]))) {
  1157. X        printf("%s: owner \"%s\" doesn\'t exist on this machine\n", progname, apcUid[iWho]);
  1158. X        exit(17);
  1159. X    }
  1160. X    (void) setgrent();
  1161. X    if ((struct group *)0 == (pgr = getgrnam(apcGroup[iWho]))) {
  1162. X        printf("%s: group \"%s\" doesn\'t exist on this machine\n", progname, apcGroup[iWho]);
  1163. X        exit(17);
  1164. X    }
  1165. X
  1166. X    (void)sprintf(acPath, "%s/%s/%s", ppw->pw_dir, apcDir[iWho], PROJLIST);
  1167. X    ReadProjects(acPath);
  1168. X    if (LISTPROJ == eWhat) {
  1169. X        ListProjects(pcCourse);
  1170. X        exit(0);
  1171. X    }
  1172. X
  1173. X    pcProj = ProjName(pcProj, pcCourse);
  1174. X    GoodFile(pcProj, "project");
  1175. X
  1176. X    if ((char *)0 == pcDiv || '\000' == pcDiv[0]) {
  1177. X        static char acDiv[MAXDIVSEC+1];
  1178. X        pcDiv = acDiv;
  1179. X        *pcDiv = '\000';
  1180. X        FindSection(iWho, stdout, pcDiv);
  1181. X    }
  1182. X    GoodFile(pcDiv, "division/section");
  1183. X
  1184. X    (void)sprintf(acPath, "%s/%s/%s", ppw->pw_dir, apcDir[iWho], pcProj);
  1185. X    if (0 != strcmp(acSectIgnore, pcDiv) || 0 != strcmp(acSectIgnore, apcSections[iWho])) {
  1186. X        (void)strcat(acPath, "/");
  1187. X        (void)strcat(acPath, pcDiv);
  1188. X    }
  1189. X    /* become the instructor
  1190. X     */
  1191. #ifdef HP_UX
  1192. X        if (-1 == setresgid(-1, pgr->gr_gid, -1) || -1 == setresuid(-1, ppw->pw_uid, -1)) {
  1193. X        fprintf(stderr, "%s: setres{u,g}id: %s\n", progname, strerror(errno));
  1194. #else
  1195. X    if (-1 == setegid(pgr->gr_gid) || -1 == seteuid(ppw->pw_uid)) {
  1196. X        fprintf(stderr, "%s: sete{u,g}id: %s\n", progname, strerror(errno));
  1197. #endif
  1198. X        exit(20);
  1199. X    }
  1200. X
  1201. X    if (0 != stat(acPath, & stat_buf)) {
  1202. X        fprintf(stderr, "%s: stat: %s: %s\n", progname, acPath, strerror(errno));
  1203. X        exit(18);
  1204. X    }
  1205. X    strcat(acPath, "/");
  1206. X
  1207. X    /* preserve $USER or $LOGNAME if uid of that name matches our uid
  1208. X     * this is a gratuitous feature no one will ever use... (ksb)
  1209. X     */
  1210. X    if ((char *)0 == (pcUser = getenv("USER"))) {
  1211. X        pcUser = getenv("LOGNAME");
  1212. X    }
  1213. X    if ((char *)0 != pcUser && (struct passwd *)0 != (ppw = getpwnam(pcUser)) && getuid() == ppw->pw_uid) {
  1214. X        strcat(acPath, pcUser);
  1215. X    } else if ((struct passwd *)0 != (ppw = getpwuid(getuid()))) {
  1216. X        strcat(acPath, ppw->pw_name);
  1217. X    } else {
  1218. X        fprintf(stderr, "%s: getpwuid(%d): %s\n", progname, getuid(), strerror(errno));
  1219. X        exit(1);
  1220. X    }
  1221. X
  1222. X    if (0 != fVerbose && optind == argc) {
  1223. X        ShowTar(acPath);
  1224. X        exit(0);
  1225. X    }
  1226. #if CONFIRM
  1227. X    if (!isatty(fileno(stdin)) || !isatty(fileno(stdout))) {
  1228. X        /* do not check, we are not interactive */;
  1229. X    } else if (0 == stat(acPath, & stat_buf)) {
  1230. X        printf("%s: overwrite previously submitted project? [yn] ", progname);
  1231. X        if ((char *)0 != GetWord(acYes, MAXCHARS, "yes") && ! ('y' == acYes[0] || 'Y' == acYes[0]))
  1232. X            exit(0);
  1233. X    } else {
  1234. X        strcat(acPath, acDotZ);
  1235. X        if (0 == stat(acPath, & stat_buf)) {
  1236. X            printf("%s: overwrite previously submitted project? [yn] ", progname);
  1237. X            if ((char *)0 != GetWord(acYes, MAXCHARS, "yes") && ! ('y' == acYes[0] || 'Y' == acYes[0]))
  1238. X                exit(0);
  1239. X        }
  1240. X        acPath[strlen(acPath)-sizeof(acDotZ)+1] = '\000';
  1241. X    }
  1242. #endif
  1243. X    (void)unlink(acPath);    /* no symlink to another file    */
  1244. X
  1245. X        /*
  1246. X        ** trash any former compressed files
  1247. X        */
  1248. X    strcat(acPath, ".Z");
  1249. X    (void)unlink(acPath);
  1250. X    acPath[strlen(acPath)-2] = '\000';
  1251. X
  1252. X    if ((char **)0 == (nargv = (char **)calloc(argc+6, sizeof(char *)))) {
  1253. X        fprintf(stderr, "%s: out of memory\n", progname);
  1254. X        exit(18);
  1255. X    }
  1256. X    nargv[0] = "tar";
  1257. X    nargv[1] = "chbf";
  1258. X    nargv[2] = "1";
  1259. X    nargv[3] = "-";
  1260. X    for (i = optind; i < argc; ++i) {    /* copy args for tar    */
  1261. X        if ('/' == *argv[i]) {
  1262. X            fprintf(stderr, "%s: full pathnames not allowed on submitted files\n", progname);
  1263. X            exit(19);
  1264. X        }
  1265. X        nargv[i+4-optind] = argv[i];
  1266. X    }
  1267. X
  1268. X    fflush(stdout);
  1269. X    fflush(stderr);
  1270. X    switch (pid = fork()) {            /* child is tar        */
  1271. X    case -1:
  1272. X        fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
  1273. X        exit(21);
  1274. X    case 0:
  1275. X        (void)umask(0077);
  1276. X        close(1);
  1277. X        if (1 != open(acPath, O_CREAT|O_TRUNC|O_WRONLY, 0600)) {
  1278. X            fprintf(stderr, "%s: open: %s: %s\n", progname, acPath, strerror(errno));
  1279. X            exit(22);
  1280. X        }
  1281. X        if (-1 == setuid(getuid()) || -1 == setgid(getgid())) {
  1282. X            fprintf(stderr, "%s: set{g,u}id: %s\n", progname, strerror(errno));
  1283. X            exit(1);
  1284. X        }
  1285. X        execve(acTar, nargv, environ);
  1286. X        fprintf(stderr, "%s: exec: %s: %s\n", progname, acTar, strerror(errno));
  1287. X        exit(21);
  1288. X    default:
  1289. X        while (-1 != (n = wait(& wait_buf)) && pid != n)
  1290. X            ;
  1291. X        if (-1 == n) {
  1292. X            fprintf(stderr, "%s: wait: %s\n", progname, strerror(errno));
  1293. X            exit(22);
  1294. X        }
  1295. X        if (0 != wait_buf.w_retcode) {
  1296. X            fprintf(stderr, "%s: tar failed (%d)\n", progname, wait_buf.w_retcode);
  1297. X            exit((int)wait_buf.w_retcode);
  1298. X        }
  1299. X        break;
  1300. X    }
  1301. X
  1302. X    /*still instructor here*/
  1303. X    if (0 != stat(acPath, & stat_buf)) {
  1304. X        fprintf(stderr, "%s: stat: %s: %s\n", progname, acPath, strerror(errno));
  1305. X        exit(18);
  1306. X    }
  1307. X    if (0 != fVerbose) {
  1308. X        ShowTar(acPath);
  1309. X    }
  1310. X    if (stat_buf.st_size > (off_t) BIGTAR) {
  1311. X        printf("Compressing submitted files... please wait\n");
  1312. X        fflush(stdout);
  1313. X        nargv[0] = "compress";
  1314. X        nargv[1] = "-f";
  1315. X        nargv[2] = acPath;
  1316. X        nargv[3] = (char *)0;
  1317. X        switch (pid = fork()) {        /* child is compress    */
  1318. X        case -1:
  1319. X            fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
  1320. X            exit(21);
  1321. X        case 0:
  1322. X            (void)umask(0077);
  1323. X            execve(acCompress, nargv, environ);
  1324. X            fprintf(stderr, "%s: execve: %s: %s\n", progname, acCompress, strerror(errno));
  1325. X            exit(21);
  1326. X        default:
  1327. X            while (-1 != (n = wait(& wait_buf)) && pid != n)
  1328. X                ;
  1329. X            if (-1 == n) {
  1330. X                fprintf(stderr, "%s: wait: %s\n", progname, strerror(errno));
  1331. X                exit(22);
  1332. X            }
  1333. X            if (0 != wait_buf.w_retcode) {
  1334. X                fprintf(stderr, "%s: compress failed (%d)\n", progname, wait_buf.w_retcode);
  1335. X                exit((int)wait_buf.w_retcode);
  1336. X            }
  1337. X            break;
  1338. X        }
  1339. X    } else {
  1340. X        /* remove any old, larger file */
  1341. X        (void) strcat(acPath, acDotZ);
  1342. X        (void) unlink(acPath);
  1343. X    }
  1344. X
  1345. X    printf("Your files have been submitted to %s, ", pcCourse);
  1346. X    if (isdigit(*pcProj)) {
  1347. X        printf("project ");
  1348. X    }
  1349. X    printf("%s for grading.\n", pcProj);
  1350. X
  1351. X    (void) endpwent();
  1352. X    (void) endgrent();
  1353. X    exit(0);
  1354. }
  1355. Purdue
  1356. chmod 0444 turnin/turnin.c ||
  1357. echo 'restore of turnin/turnin.c failed'
  1358. Wc_c="`wc -c < 'turnin/turnin.c'`"
  1359. test 30395 -eq "$Wc_c" ||
  1360.     echo 'turnin/turnin.c: original size 30395, current size' "$Wc_c"
  1361. fi
  1362. # ============= project/project.1l ==============
  1363. if test ! -d 'project'; then
  1364.     echo 'x - creating directory project'
  1365.     mkdir 'project'
  1366. fi
  1367. if test -f 'project/project.1l' -a X"$1" != X"-c"; then
  1368.     echo 'x - skipping project/project.1l (File already exists)'
  1369. else
  1370. echo 'x - extracting project/project.1l (Text)'
  1371. sed 's/^X//' << 'Purdue' > 'project/project.1l' &&
  1372. .TH PROJECT 1L PUCC
  1373. .SH NAME
  1374. project \- control electronic submission
  1375. .SH SYNOPSIS
  1376. \fBproject\fP [\fB\-dehilnqrvV\fP] [\fB\-c\fP\fIcourse\fP] [\fB\-G\fP\fIcmd\fP] [\fB\-g\fP\fIcmd\fP] [\fIproject\-name\fP]
  1377. .br
  1378. .SH DESCRIPTION
  1379. .I Project
  1380. first determines for which class the invoking user is receiving projects.
  1381. If \fIproject\fP does not find the invoker's login name in its database,
  1382. it aborts.  Otherwise,
  1383. .I project
  1384. looks to see what projects this course has and which one is current.
  1385. .PP
  1386. If
  1387. .I project
  1388. is run without options and no 
  1389. .IR project\-name ,
  1390. it displays a help message.  If
  1391. .I project
  1392. is initiated without options but with a 
  1393. .IR project\-name ,
  1394. it will look for
  1395. the submission directory \fIsubmit\fP
  1396. (note: this directory may have any name)
  1397. in the home directory of the user.
  1398. If \fIsubmit\fP does not exist,
  1399. .I project
  1400. will create one.
  1401. .I Project
  1402. then modifies the file \fIProjlist\fP to enable submissions for the named
  1403. project and
  1404. creates a
  1405. subdirectory in \fIsubmit\fP with the same name as the project being
  1406. enabled
  1407. to receive the submitted projects. 
  1408. Submission permissions for the new project are unchanged.
  1409. .PP
  1410. Project names may not have a period in them, but they may contain letters,
  1411. numbers, or other marks of punctuation.
  1412. .PP
  1413. .I Project
  1414. invoked with options but without a
  1415. .IR project\-name ,
  1416. uses the file \fIProjlist\fP in the submission directory to determine
  1417. the current project name.
  1418. .PP
  1419. The invoker may use the options below to disable, enable, grade, and remove
  1420. the submitted files.  The submitted files are stored in a tar format file
  1421. named for the login name which submitted them (see turnin(5L)).
  1422. .SH OPTIONS
  1423. .TP
  1424. .BI \-c course
  1425. Project, when run by the superuser, will use this \fIcourse\fP to
  1426. select the effective user id.
  1427. Normal users may use this option to administer more than one course.
  1428. (If the selected user doesn't exist on this machine, project will exit
  1429. nonzero.)
  1430. .TP
  1431. .B \-d
  1432. Disable submissions for the current project.
  1433. .TP
  1434. .B \-e
  1435. Enable submissions for the current project and make the current project the
  1436. default project.
  1437. .TP
  1438. .B \-l
  1439. Enable submissions for the current project but do not change the default
  1440. project.
  1441. .TP
  1442. .BI \-G cmd
  1443. Execute the given command once for every division of the class.
  1444. Note that the \fIcmd\fP must be in the search path of the invoker.
  1445. See \fB\-G\fP below for escape sequences, except %u expands to ``*''.
  1446. If both \fB\-g\fP and \fB\-G\fP are given all the \fB\-g\fP commands
  1447. for a division are done before the \fB\-G\fP command for that division.
  1448. .TP
  1449. .BI \-g cmd
  1450. Execute the given command once for every submitted assignment.
  1451. Note that the \fIcmd\fP must be in the search path of the invoker.
  1452. Several escape sequences are used to pass arguments to
  1453. .I cmd ,
  1454. all begin with a percent sign (%), they are:
  1455. .br
  1456. .TS
  1457. rw(8) l.
  1458. h    home directory of the instructor
  1459. t    turnin directory name
  1460. p    current project
  1461. d    division/section identifier for the current student
  1462. u    current students login name
  1463. s    the full path name of the associated tar file
  1464. %    yields a percent sign
  1465. .TE
  1466. .TP
  1467. .B \-h
  1468. Display a summary of 
  1469. .I project's
  1470. options.
  1471. .TP
  1472. .B \-i
  1473. \fBProject\fP will ask the user about how they wish to use the
  1474. electronic submission system and initialize their account.
  1475. Note that mail is sent to a superuser to do the final configuration,
  1476. so this command is not immediate.
  1477. An example of this option may be found in /usr/doc/local/project/install.
  1478. .TP
  1479. .B \-n
  1480. Print what would be done but don't do it.
  1481. This option implies the \-\fBv\fP option.
  1482. .TP
  1483. .B \-o
  1484. Display a status line on the terminal.
  1485. .TP
  1486. .B \-q
  1487. \fBProject\fP confirms the command then removes all the project
  1488. related files in the user's account.
  1489. .TP
  1490. .B \-r
  1491. Remove
  1492. .I all
  1493. files associated with the current project number.
  1494. .I Project
  1495. will ask you to verify that you really want to do this before proceeding.
  1496. .TP
  1497. .B \-v
  1498. Verbose.  Print shell commands as they are executed.
  1499. .TP
  1500. .B \-V
  1501. Show which version of \fIproject\fP is running.
  1502. .SH EXAMPLES
  1503. .TP
  1504. project \-e project4
  1505. Enable submissions for project 4.
  1506. .TP
  1507. project \-d parser
  1508. Turn off all new submissions for the parser project.
  1509. .TP
  1510. project \-g "echo %u"
  1511. List all the login names of the students that have submitted something for
  1512. the current project.
  1513. .TP
  1514. project \-g \*(lq\fIgradeit\fP %s\*(rq 4
  1515. Grade all submitted files with a shell script named \fIgradeit\fP.
  1516. .sp
  1517. Note that the script is usually passed the full path name to the
  1518. submitted files which are kept in a single tar format file.  The script
  1519. that is doing the grading will make a temporary directory to unpack
  1520. and compile the submitted code (see FILES).
  1521. .TP
  1522. project \-g \*(lqif [ d1s1 = %d ] ; then gradeit %s ; fi\*(rq
  1523. Grad only division 1 section 2 with a grading script (gradeit).
  1524. .sp 1
  1525. The use of the shell if statement in the \fIcmd\fP is to prevent
  1526. the unnecessary invocation of the grading script. The script, however,
  1527. could do the comparison itself using exit if the division/section
  1528. identifier did not match an ideal one.
  1529. .sp 1
  1530. For an instructor to disable submission for project 3 at around
  1531. midnight on October 15 for all hosts mentor.cc, expert.cc,
  1532. sage.cc, (from sage.cc):
  1533. .br
  1534. X    at \-m 0005 Oct 15
  1535. .br
  1536. X    at> project \-d project3
  1537. .br
  1538. X    at> rsh mentor project \-d project3
  1539. .br
  1540. X    at> rsh expert project \-d project3
  1541. .br
  1542. X    at> ^D
  1543. .br
  1544. (these commands could be put into a file).
  1545. .br
  1546. .TP
  1547. project \-i
  1548. Set up this account to receive submissions.
  1549. .TP
  1550. project \-V
  1551. project: $\&Id: project.c,v 4.6 90/07/02 14:18:32 ksb Exp $
  1552. .br
  1553. project: default directory name: submit
  1554. .SH FILES
  1555. /usr/doc/local/project/template is a sample shell script to grade Pascal programs.
  1556. .br
  1557. /usr/doc/local/project/install is a sample installation session.
  1558. .SH BUGS
  1559. .PP
  1560. .I Project
  1561. .B must
  1562. be run on every machine which you wish students to submit files.
  1563. .sp
  1564. Attempts to copy the project submission directory or the
  1565. \fIProjlist\fI file to other hosts will cause problems when
  1566. students use
  1567. .IR turnin (1L)
  1568. to turn in their projects.
  1569. .SH SEE ALSO
  1570. at(1), turnin(1L), sh(1), tar(1), environ(7)
  1571. Purdue
  1572. chmod 0444 project/project.1l ||
  1573. echo 'restore of project/project.1l failed'
  1574. Wc_c="`wc -c < 'project/project.1l'`"
  1575. test 5973 -eq "$Wc_c" ||
  1576.     echo 'project/project.1l: original size 5973, current size' "$Wc_c"
  1577. fi
  1578. # ============= turnin/INSTALL ==============
  1579. if test -f 'turnin/INSTALL' -a X"$1" != X"-c"; then
  1580.     echo 'x - skipping turnin/INSTALL (File already exists)'
  1581. else
  1582. echo 'x - extracting turnin/INSTALL (Text)'
  1583. sed 's/^X//' << 'Purdue' > 'turnin/INSTALL' &&
  1584. Turnin(1L) and project(1L) provide administration of electronic
  1585. submissions for a large number of classes.  These tools may be applied
  1586. to any type of course (programming, art & design, word processing, etc.)
  1587. which requires `students' to submit `projects' to an `instructor'.
  1588. X
  1589. A `student' is any login name on a UNIX system.  Multiple logins under
  1590. the same uid are handled correctly.  The environment variables USER and
  1591. LOGNAME are consulted (in that order) to determine which login name is
  1592. submitting the file.
  1593. X
  1594. A `project' is a broad term for a group of submissions, one per login
  1595. name, which will be collected over time.  These may then be processed
  1596. by running a shell command for each project.   Submitted files are
  1597. stored in a tar(1) format archive.  Large archives are kept compressed,
  1598. but nether user nor administrator will see this detail.
  1599. X
  1600. An `instructor' is a supervising login name.  This login is able to
  1601. limit the collection time for any `project' and issue grade commands.
  1602. X
  1603. The user interface to turnin(1L) is quite simple and obvious.  The user
  1604. simply runs
  1605. X
  1606. X    $ turnin files
  1607. X
  1608. to submit files.  If the request is ambiguous turnin will request
  1609. information from stdin.  Command line options are provides to suppress
  1610. such prompting.  Some profs use a simple (1 line) shell script to force
  1611. the course option on the user.  For example cs444 might use a script
  1612. `sub444' which read:
  1613. X
  1614. X    #!/bin/sh turnin -c cs444 "$@"
  1615. X
  1616. to `simplify' the user environment.
  1617. X
  1618. The user interface to project(1L) also quite simple.  The administrator
  1619. may enable a project with the -e option:
  1620. X
  1621. X    project -e btrees
  1622. X
  1623. would enable a project called `btrees' and disable a project with the
  1624. -d option:
  1625. X
  1626. X    project -d queues
  1627. X
  1628. After submissions for a project are complete (or even while some are
  1629. still coming in) the instructor may use the -g option to scan the
  1630. submitted files with a shell command.  The shell command is run once
  1631. for each login name who has submitted files.  Some percent (%) escapes
  1632. are substituted before the command is given to the shell.
  1633. X
  1634. X    project -g "echo %u" queues
  1635. X
  1636. would scan the project `queues' and echo the login name of each user
  1637. who has submitted files.
  1638. X
  1639. Of course there are other options to project, but these three are by
  1640. far the most common.
  1641. X
  1642. X
  1643. Setting up the system.
  1644. X
  1645. The superuser must install 3 files in the user-accessible portion of
  1646. the UNIX system.
  1647. X
  1648. The binaries for project and turnin should be installed in the system's
  1649. default search path in something like /usr/local/bin or /usr/custom/bin.
  1650. The project binary should be mode 0755 (or 0711), the turnin binary
  1651. should be mode 04755 (or 04711) and owned by the superuser.  This is
  1652. because turnin must set-effective-uid to the target instructor on the
  1653. fly.  (It could be setuid to a single instructor, but the program is
  1654. useful to many instructors.)
  1655. X
  1656. There is a configuration file which should be superuser-writable only
  1657. (mode 0644, owned by the superuser).  This file tells the system which
  1658. logins are available to accept projects.  This file is installed in a
  1659. lib directory, usually /usr/local/lib or /usr/custom/lib, and is called
  1660. turnin.cf.  This file contains a colon-separated list of data very much
  1661. like the passwd file; there is a manual page for these fields
  1662. (turnin.cf.5L).
  1663. X
  1664. Purdue
  1665. chmod 0444 turnin/INSTALL ||
  1666. echo 'restore of turnin/INSTALL failed'
  1667. Wc_c="`wc -c < 'turnin/INSTALL'`"
  1668. test 3242 -eq "$Wc_c" ||
  1669.     echo 'turnin/INSTALL: original size 3242, current size' "$Wc_c"
  1670. fi
  1671. # ============= turnin/turnin.1l ==============
  1672. if test -f 'turnin/turnin.1l' -a X"$1" != X"-c"; then
  1673.     echo 'x - skipping turnin/turnin.1l (File already exists)'
  1674. else
  1675. echo 'x - extracting turnin/turnin.1l (Text)'
  1676. sed 's/^X//' << 'Purdue' > 'turnin/turnin.1l' &&
  1677. .\" $Compile: ${nroff-nroff} -man %f |${PAGER-${more-more}}
  1678. .\" $Laser: ${ltroff-ltroff} -man %f
  1679. .TH TURNIN 1L
  1680. .UC 4
  1681. .SH NAME
  1682. turnin \- electronically submit files for grading
  1683. .SH SYNOPSIS
  1684. .B turnin
  1685. [\fB\-hilsvV\fP] [\fB\-c\fP\fIcourse\fP] [\fB\-p\fP\fIproject\fP]
  1686. .I files
  1687. .br
  1688. .SH DESCRIPTION
  1689. .I Turnin
  1690. reads each of
  1691. .I files
  1692. in sequence and copies it to the directory designated by the course's
  1693. instructor.  If the file being read is a directory,
  1694. .I turnin
  1695. will recursively copy the entire contents of the directory.
  1696. .PP
  1697. In order for
  1698. .I turnin
  1699. the instructor must have submissions turned on, see \fIproject\fP(1L).
  1700. .PP
  1701. Any previous submissions for the current project are overwritten.
  1702. .SH OPTIONS
  1703. .TP
  1704. \-c \fIcourse\fR
  1705. Specify the course.  Must be used if the student is in more than one course
  1706. that uses \fIturnin\fR to collect assignments.  The student will be prompted
  1707. for the \fIcourse\fR if they are enrolled in more than one and this option
  1708. isn't used.
  1709. .TP
  1710. \-h
  1711. Outputs a terse usage message.
  1712. .TP
  1713. \-i
  1714. In this case \fIturnin\fP doesn't actually submit any files.
  1715. Any \fIfiles\fP given are taken to be course names for which to produce
  1716. envrironment settings.
  1717. If no \fIfiles\fP are given the user it prompted to list all the courses
  1718. he is currently enrolled in.
  1719. The shell commands output on stdout are appropriate to the users SHELL
  1720. environment variable.
  1721. .TP
  1722. \-l
  1723. Lists the projects for the specified class.  Projects open for submissions
  1724. and ones closed to submissions are listed.
  1725. .TP
  1726. \-p \fIproject\fR
  1727. Submit \fIfiles\fP to the specified \fIproject\fP. 
  1728. .TP
  1729. \-s
  1730. Lists the classes that the student is enrolled in.
  1731. .TP
  1732. \-v
  1733. .I Turnin
  1734. displays a \`tar tvf\' of the submitted files when this option is
  1735. specified (either with or without submitting any files).
  1736. .TP
  1737. \-V
  1738. Show the RCS Id for this version of \fIturnin\fP.
  1739. .SH EXAMPLES
  1740. .TP
  1741. turnin -c cs100 factorial.p
  1742. Submit the file \*(lqfactorial.p\*(rq to the instructor for cs100.
  1743. .TP
  1744. turnin -v Makefile tour.c passport.c
  1745. Submit three files to the default course
  1746. (or prompt for the course to submit to)
  1747. then display a list of the submitted files.
  1748. .TP
  1749. turnin -v
  1750. Display a list of the files this user most recently submitted
  1751. (for the default project).
  1752. .TP
  1753. turnin -v -p project1-late
  1754. Display a list of the files this user most recently submitted
  1755. for \*(lqproject1-late\*(rq.
  1756. .TP
  1757. turnin -i cs100 >>.login
  1758. Add a \fIcsh\fP(1) command to the users .login to set the users section
  1759. for cs100.
  1760. .TP
  1761. turnin -V
  1762. turnin: $\&Id: turnin.c,v 5.0 90/06/22 11:43:02 ksb Exp $
  1763. .SH "SEE ALSO"
  1764. csh(1), project(1L), sh(1), tar(1)
  1765. Purdue
  1766. chmod 0444 turnin/turnin.1l ||
  1767. echo 'restore of turnin/turnin.1l failed'
  1768. Wc_c="`wc -c < 'turnin/turnin.1l'`"
  1769. test 2553 -eq "$Wc_c" ||
  1770.     echo 'turnin/turnin.1l: original size 2553, current size' "$Wc_c"
  1771. fi
  1772. # ============= turnin.cf/turnin.5l ==============
  1773. if test ! -d 'turnin.cf'; then
  1774.     echo 'x - creating directory turnin.cf'
  1775.     mkdir 'turnin.cf'
  1776. fi
  1777. if test -f 'turnin.cf/turnin.5l' -a X"$1" != X"-c"; then
  1778.     echo 'x - skipping turnin.cf/turnin.5l (File already exists)'
  1779. else
  1780. echo 'x - extracting turnin.cf/turnin.5l (Text)'
  1781. sed 's/^X//' << 'Purdue' > 'turnin.cf/turnin.5l' &&
  1782. .\"
  1783. .TH TURNIN 5L
  1784. .UC 4
  1785. .SH NAME
  1786. turnin \- instructions for instructors who use
  1787. .IR turnin ( 1L )
  1788. and
  1789. .IR project ( 1L )
  1790. .br
  1791. .SH DESCRIPTION
  1792. .PP
  1793. .IR Turnin ( 1L )
  1794. is a program provided by Purdue University Computing Center to
  1795. aid instructors in the collecting of assignments from students.
  1796. To use
  1797. .IR turnin ,
  1798. the instructor must run the command
  1799. .sp 1
  1800. X    project -i
  1801. .sp 1
  1802. which will interactively prompt the instructor for more information about
  1803. the course in question.
  1804. The superuser will be notified that the instructor wishes to use
  1805. turnin and will add the user's login name to a configuration file.
  1806. .PP
  1807. The Computing Center may also build a .login (.profile) in each of
  1808. your students accounts to create a initial csh (sh) environment.
  1809. In this .login (.profile), there will be an environment variable
  1810. called \*(lqSUB_\fIcourse\fP\*(rq, where \fIcourse\fP is the course that
  1811. the student is enrolled in.
  1812. This environment variable is set to the student's division or section
  1813. to allow for large courses to be managed effectively.
  1814. For example the command:
  1815. .sp 1
  1816. X    setenv SUB_cs300 morning
  1817. .sp 1
  1818. might be used to put a given user's files into the `morning' section
  1819. for cs300 (this command would be in the user's generated .login).
  1820. .PP
  1821. The project command maintains a directory hierarchy in the course
  1822. administrator's account which \fIturnin\fP(1L) will modify to 
  1823. submit files for a login.
  1824. This directory tree is rooted at the name given to the project command
  1825. when it was run with the -i option and must be world readable.
  1826. The default name for the root of this tree is `submit'.
  1827. .PP
  1828. Here is a sample directory layout:
  1829. .sp 1
  1830. .ta 0.7i 1.4i 2.1i 2.8i 3.5i 4.2i 4.9i 5.6i
  1831. X    $ cd
  1832. .br
  1833. X    $ ls -aF submit
  1834. .br
  1835. X    \&./    \&../    \&.lock    Projlist    1/    2/
  1836. .br
  1837. X    $ ls -aF submit/1
  1838. .br
  1839. X    \&./    \&../    morning/    afternoon/
  1840. .br
  1841. X    $ ls -aF submit/1/morning
  1842. .br
  1843. X    \&./    \&../    dsg    ksb    mjb    mjs
  1844. .br
  1845. X    $ exit
  1846. .sp 1
  1847. .PP
  1848. At the top level there are 3 types of entries:  a lock file (`.lock')
  1849. which is \fIflock\fP(2)'d when project is running, a database file
  1850. (`Projlist') which contains the status information on the current
  1851. projects, and a directory for each active project.
  1852. .PP
  1853. Each active project directory contains a subdirectory for each division or
  1854. section of the course.
  1855. Each of these, in turn, contains one \fItar\fP(1) file for each login
  1856. name that has submitted files for this project.
  1857. .SH BUGS
  1858. A student may change sections (or divisions) by changing an environment
  1859. variable.
  1860. This generally leads to a grader not getting a submission from the student.
  1861. .SH AUTHOR
  1862. Kevin S Braunsdorf, PUCC (ksb@cc.purdue.edu, pur-ee!ksb, purdue!ksb)
  1863. .SH "SEE ALSO"
  1864. project(1L), turnin(1L), tar(1), turnin.cf(5L)
  1865. Purdue
  1866. chmod 0444 turnin.cf/turnin.5l ||
  1867. echo 'restore of turnin.cf/turnin.5l failed'
  1868. Wc_c="`wc -c < 'turnin.cf/turnin.5l'`"
  1869. test 2655 -eq "$Wc_c" ||
  1870.     echo 'turnin.cf/turnin.5l: original size 2655, current size' "$Wc_c"
  1871. fi
  1872. # ============= project/template ==============
  1873. if test -f 'project/template' -a X"$1" != X"-c"; then
  1874.     echo 'x - skipping project/template (File already exists)'
  1875. else
  1876. echo 'x - extracting project/template (Text)'
  1877. sed 's/^X//' << 'Purdue' > 'project/template' &&
  1878. #!/bin/sh
  1879. # $1 is our only argument, a %s (full path to tar file)
  1880. #  we were envoked from project -g with something like
  1881. #    project -g "grade7 %s" seven
  1882. X
  1883. # you should replace the "cs101" below with a unique course id (3-7 chars)
  1884. COURSE=cs101
  1885. X
  1886. # since we start off in the division/section directory of the student
  1887. # we need to get to someplace to make a temp directory
  1888. # ($$ is a randon number, see sh(1))
  1889. cd /tmp
  1890. mkdir $COURSE$$
  1891. cd $COURSE$$
  1892. X
  1893. # now output a message indicating who we are working on this file
  1894. echo "----------"
  1895. echo "--  $1"
  1896. echo "----------"
  1897. tar xvf $1
  1898. echo ""
  1899. X
  1900. # remove crufty a.out he may have submitted (by mistake, of course)
  1901. rm -f a.out core
  1902. X
  1903. # compile the project in your favorite language
  1904. pc -l *.p
  1905. #cc *.c
  1906. #f77 *.f
  1907. #fortran *.f
  1908. #make a.out
  1909. X
  1910. # we could check to see what langauge he used, like C or Pacal
  1911. #if [ -f Makefile -o -f makefile ]; then
  1912. #    make a.out
  1913. #elif [ "`echo *.c`" != "*.c" ]; then
  1914. #    cc *.c
  1915. #elif [ "`echo *.p`" != "*.p" ]; then
  1916. #    pc *.c
  1917. #else
  1918. #    echo "unrecognized language (not C or Pascal)"
  1919. #    ls
  1920. #fi
  1921. X
  1922. # if it did not build an a.out he looses
  1923. if [ -f ./a.out ]; then
  1924. X    ./a.out                 ; : "(#) see below"
  1925. X    if [ -f core ]; then
  1926. X        echo "DROPPED CORE"
  1927. X    fi
  1928. else
  1929. X    echo "NO a.out generated"
  1930. fi
  1931. X
  1932. # (#) above we could use lots of command to check for things
  1933. # we could draw input from a data file
  1934. #    ./a.out < /usere/ksb/data2
  1935. # which could be argument $2
  1936. #    ./a.out < $2
  1937. #
  1938. # we can limit resources through a csh line (see csh(1))
  1939. #    csh -fc "limit cpu 30; ./a.out"
  1940. #
  1941. # we can run many files
  1942. #    for DATA_FILE in /usera/ksb/data/d7.*
  1943. #    do
  1944. #        ./a.out <$DATA_FILE
  1945. #    done
  1946. #
  1947. # we can mix any of the above
  1948. X
  1949. # now we remove the compiled program so we don't fill up a file system!
  1950. cd ..
  1951. rm -rf $COURSE$$
  1952. X
  1953. # and exit with a happy face (important in the future)
  1954. Xexit 0
  1955. Purdue
  1956. chmod 0444 project/template ||
  1957. echo 'restore of project/template failed'
  1958. Wc_c="`wc -c < 'project/template'`"
  1959. test 1781 -eq "$Wc_c" ||
  1960.     echo 'project/template: original size 1781, current size' "$Wc_c"
  1961. fi
  1962. # ============= project/machine.h ==============
  1963. if test -f 'project/machine.h' -a X"$1" != X"-c"; then
  1964.     echo 'x - skipping project/machine.h (File already exists)'
  1965. else
  1966. echo 'x - extracting project/machine.h (Text)'
  1967. sed 's/^X//' << 'Purdue' > 'project/machine.h' &&
  1968. /*
  1969. X * These might be locally tuned                        (ksb)
  1970. X */
  1971. /* #define bsd        /**/
  1972. /* #define HPUX7    /**/
  1973. /* #define pdp11    /**/
  1974. X
  1975. /*
  1976. X * send reports of requests for project additions:
  1977. X */
  1978. #if !defined(MAILTO)
  1979. #define MAILTO        "enroll-info"
  1980. #endif
  1981. X
  1982. /*
  1983. X * where the global database is kept for who can ctrl project(s)
  1984. X */
  1985. #if ! defined(TURNBASE)
  1986. #define TURNBASE    "/usr/local/lib/turnin.cf"
  1987. #endif
  1988. X
  1989. /*
  1990. X * where the list of active projects is kept in the submit directory
  1991. X */
  1992. #if ! defined(PROJLIST)
  1993. #define PROJLIST    "Projlist"
  1994. #endif
  1995. X
  1996. #if !defined(SECTIGNORE)
  1997. #define SECTIGNORE    "ALL"
  1998. #endif
  1999. X
  2000. #if ! defined(LOCKFILE)
  2001. #define LOCKFILE    ".lock"
  2002. #endif
  2003. X
  2004. #if ! defined(SUBPREFIX)
  2005. #define SUBPREFIX    "SUB_"
  2006. #endif
  2007. X
  2008. #if ! defined(COMPRESS_PATH)
  2009. #if defined(HPUX7)
  2010. #define COMPRESS_PATH    "/usr/bin/compress"
  2011. #else
  2012. #define COMPRESS_PATH    "/usr/ucb/compress"
  2013. #endif
  2014. #endif
  2015. X
  2016. #if ! defined(MAILX_PATH)
  2017. #if defined(HPUX7)
  2018. #define MAILX_PATH    "/usr/bin/mailx"
  2019. #else
  2020. #define MAILX_PATH    "/usr/ucb/Mail"
  2021. #endif
  2022. #endif
  2023. X
  2024. #define BIGTAR        5000    /* size of a large project (to compress)*/
  2025. X
  2026. #if ! defined(TAR_PATH)
  2027. #if defined(HPUX7)
  2028. #define TAR_PATH    "/usr/bin/tar"
  2029. #else
  2030. #define TAR_PATH    "/bin/tar"
  2031. #endif
  2032. #endif
  2033. X
  2034. /*
  2035. X * These might not change much                        (ksb)
  2036. X */
  2037. #define MAXCHARS    2000    /* maximum line length in database    */
  2038. #define SEP        ':'    /* field separator for data base    */
  2039. #define MAXCOURSENAME    32    /* max width of a course name        */
  2040. #define MAXLOGINNAME    32    /* max chars in a login name        */
  2041. #define MAXSUBDIRNAME    48    /* max chars in a subdir name        */
  2042. #define MAXGROUPNAME    32    /* max chars in a group name        */
  2043. #define MAXDIVSEC    32    /* max width of a div/sec name        */
  2044. X
  2045. /* not kept in an online database -- no one nees it ?
  2046. X */
  2047. #define MAXLONGNAME    132    /* long name for the superuser        */
  2048. #define MAXYESNO    24    /* a two word explain, `none yet'    */
  2049. X
  2050. #define MAXPROJNAME    32    /* maximum chars in a project name    */
  2051. #define MAXPROJECTS    32    /* maximum active projects/course    */
  2052. #define MAXCOURSE    64    /* maximum courses in db (total)    */
  2053. X
  2054. #if !defined(MAXPATHLEN)
  2055. #define MAXPATHLEN 1024
  2056. #endif
  2057. X
  2058. #define HAVE_NAME    defined(pdp11)
  2059. #define HAVE_STRINGS    defined(bsd)    /* 1->strings.h, 0->string.h    */
  2060. #define USE_LOCKF    defined(HPUX7)    /* 1->lockf, 0-> flock        */
  2061. X
  2062. #if defined(bsd)
  2063. #define strchr    index
  2064. #define strrchr    rindex
  2065. #endif
  2066. Purdue
  2067. chmod 0644 project/machine.h ||
  2068. echo 'restore of project/machine.h failed'
  2069. Wc_c="`wc -c < 'project/machine.h'`"
  2070. test 2259 -eq "$Wc_c" ||
  2071.     echo 'project/machine.h: original size 2259, current size' "$Wc_c"
  2072. fi
  2073. # ============= turnin/Makefile ==============
  2074. if test -f 'turnin/Makefile' -a X"$1" != X"-c"; then
  2075.     echo 'x - skipping turnin/Makefile (File already exists)'
  2076. else
  2077. echo 'x - extracting turnin/Makefile (Text)'
  2078. sed 's/^X//' << 'Purdue' > 'turnin/Makefile' &&
  2079. #    $Id: Makefile,v 4.5 90/11/23 14:10:21 becker Exp $
  2080. #
  2081. #    Makefile for turnin, depends on project (being in ../project)
  2082. #
  2083. # if you don't have strcasecmp see ../project/Makefile
  2084. X
  2085. PROG=    turnin
  2086. BIN=    ${DESTDIR}/usr/local/bin
  2087. X
  2088. PROJECT=    ../project
  2089. X
  2090. L=../libopt
  2091. #L=/usr/include/local
  2092. I=/usr/include
  2093. S=/usr/include/sys
  2094. P=
  2095. X
  2096. INCLUDE= -I$L
  2097. DEBUG=    -O
  2098. CDEFS=  
  2099. #CDEFS=    -DHP_UX
  2100. CFLAGS= ${DEBUG} ${CDEFS} ${INCLUDE}
  2101. X
  2102. HDR=    ${PROJECT}/machine.h
  2103. SRC=    turnin.c
  2104. OBJ=    turnin.o
  2105. #SRC=    turnin.c strcasecmp.c
  2106. #OBJ=    turnin.o strcasecmp.o
  2107. OTHER=    
  2108. MAN=    turnin.1l
  2109. SOURCE=    Makefile ${HDR} ${SRC} ${MAN} ${OTHER}
  2110. X
  2111. all: ${PROG}
  2112. X
  2113. ${PROG}:$P ${OBJ}
  2114. #    ${CC} -o $@ ${CFLAGS} ${OBJ} -lopt
  2115. #    ${CC} -o $@ ${CFLAGS} ${OBJ} -L /usr/local/lib -lopt
  2116. X    ${CC} -o $@ ${CFLAGS} ${OBJ} ../libopt/libopt.a
  2117. X
  2118. clean: FRC
  2119. X    rm -f Makefile.bak ${PROG} *.o a.out core errs tags
  2120. X
  2121. depend: ${HDR} ${SRC} FRC
  2122. X    maketd ${CDEFS} ${INCLUDE} ${SRC}
  2123. X
  2124. install: all FRC
  2125. X    install -cs -m 4755 -o root ${PROG} ${BIN}/${PROG}
  2126. X
  2127. lint: ${HDR} ${SRC} FRC
  2128. X    lint -h ${CDEFS} ${INCLUDE} ${SRC}
  2129. X
  2130. mkcat: ${MAN}
  2131. X    mkcat ${MAN}
  2132. X
  2133. print: source FRC
  2134. X    lpr -J'${PROG} source' ${SOURCE}
  2135. X
  2136. source: ${SOURCE}
  2137. X
  2138. spotless: clean
  2139. X    rcsclean ${SOURCE}
  2140. X
  2141. tags: ${HDR} ${SRC}
  2142. X    ctags -t ${HDR} ${SRC}
  2143. X
  2144. ${SOURCE}:
  2145. X    co -q $@
  2146. X
  2147. FRC:
  2148. X
  2149. # DO NOT DELETE THIS LINE - maketd DEPENDS ON IT
  2150. X
  2151. turnin: ../project/machine.h turnin.c
  2152. X
  2153. # *** Do not add anything here - It will go away. ***
  2154. Purdue
  2155. chmod 0644 turnin/Makefile ||
  2156. echo 'restore of turnin/Makefile failed'
  2157. Wc_c="`wc -c < 'turnin/Makefile'`"
  2158. test 1368 -eq "$Wc_c" ||
  2159.     echo 'turnin/Makefile: original size 1368, current size' "$Wc_c"
  2160. fi
  2161. true || echo 'restore of turnin.cf/turnin.cf failed'
  2162. echo End of part 1, continue with part 2
  2163. exit 0
  2164.  
  2165. exit 0 # Just in case...
  2166.